require 'parser/SymTable.rb'
require 'parser/SymVariable.rb'

# = Class ASTFunc
# Inherits from ASTStatement. Represents the implementation of an AST especifically designed
# for function declarations
# == Attributes inherited from ASTStatement
# node, children
# == Methods inherited from ASTStatement
# check(), run(), to_s_children
class ASTFunc < ASTStatement
  
  # AST representing name of the function
  attr_reader :name
  # AST representing the parameters required by the function, along with their types
  attr_reader :params
  # AST representing the return type of the function
  attr_reader :type
  # AST representing the instruction that should be executed by the function
  attr_reader :inst
  # Symbol Table for this function
  attr_reader :local_sym_table
  # Symbol containing the returned value of the function
  attr_reader :returned

  # Constructor of the class
  # === Parameters
  # * name: AST representing name of the function
  # * params: AST representing the parameters required by the function, along with their types
  # * type: AST representing the return type of the function
  # * inst: AST representing the instruction that should be executed by the function
  def initialize name, params, type, inst
    @name = name
    @params = params
    @type = type
    @inst = inst
    @local_sym_table = SymTable.new
    for param in params
      if nil != @local_sym_table.find(param.names[0].value)
        raise "El parámetro #{param.names[0].value} está siendo redeclarado"
      end
      @local_sym_table.insert(SymVariable.new(param.names[0].value, param.type))
    end
  end

  # Method to run the function
  # === Parameters
  # * parent_sym_table: Symbol table passed by the parent AST
  # * func_table: Function symbol table passed by the parent AST
  # === Return value
  # nil if executed with success or an error message
  def run param_values, func_table
    sym_table = SymTable.new
    for i in (0..param_values.length - 1)
      name = @params[i].names[0].value
      # If function is called recursively, values aren't altered
      sym_var = @local_sym_table.find(name).clone
      sym_var.value = param_values[i]
      sym_table.insert(sym_var)
    end
    msg = @inst.run sym_table, func_table
    if nil != msg
      return msg
    end
    if nil == @inst.returned
      return "La función no devolvió ningún valor"
    end
    @returned = @inst.returned
    return nil
  end

  # Method to check the static correctitude of the function
  # === Parameters
  # * parent_sym_table: Symbol table passed by the parent AST
  # * func_table: Function symbol table passed by the pare
  # * func_type: Type of valid return value
  # === Return value
  # nil if executed with success or an error message
  def check func_table
    @inst.check @local_sym_table, func_table, func_table.find(@name.node.value).type(nil)
  end

  # Returns the closest wrong token to the place were an error is generated
  def error_tk
    tk = @name
    if nil != tk
      return tk
    end
    tk = nil != @params ? @params.error_tk : nil
    if nil != tk
      return tk
    end
    tk = @type
    if nil != tk
      return tk
    end
    nil != @inst ? @inst.error_tk : nil
  end

  # Creates a 'String' representation of the AST with 'i' identation spaces
  # === Parameters
  # * i: Identation with which the AST should be printed with
  def to_s i
    im = i + @@c
    ts = @local_sym_table.to_s(i) == "" ? "" : "#{im}Tabla de símbolos:" + @local_sym_table.to_s(im + @@c)
    "(Función #{@name.node.value}: #{@type.value}\n#{ts}Instrucción: " + @inst.to_s(im) + "\n#{i})"
  end
end